home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1997 February / EnigmA AMIGA RUN 15 (1997)(G.R. Edizioni)(IT)[!][issue 1997-02][PLANET CD V].iso / enigma / earcd / emula / arosdv19.lha / AROS / exec / rawdofmt.c < prev    next >
C/C++ Source or Header  |  1996-10-24  |  10KB  |  433 lines

  1. /*
  2.     (C) 1995-96 AROS - The Amiga Replacement OS
  3.     $Id: rawdofmt.c,v 1.8 1996/10/24 15:50:54 aros Exp $
  4.     $Log: rawdofmt.c,v $
  5.     Revision 1.8  1996/10/24 15:50:54  aros
  6.     Use the official AROS macros over the __AROS versions.
  7.  
  8.     Revision 1.7  1996/10/23 14:26:05  aros
  9.     Renamed AROS macros from XYZ to AROS_XYZ, so we know what they are
  10.  
  11.     Removed UDIVMOD10()
  12.  
  13.     Revision 1.6  1996/10/19 17:07:27  aros
  14.     Include <aros/machine.h> instead of machine.h
  15.  
  16.     Revision 1.5  1996/09/13 17:51:23  digulla
  17.     Use IPTR
  18.  
  19.     Revision 1.4  1996/08/13 13:56:05  digulla
  20.     Replaced AROS_LA by AROS_LHA
  21.     Replaced some AROS_LH*I by AROS_LH*
  22.     Sorted and added includes
  23.  
  24.     Revision 1.3  1996/08/01 17:41:15  digulla
  25.     Added standard header for all files
  26.  
  27.     Desc:
  28.     Lang: english
  29. */
  30. #include <dos/dos.h>
  31. #include <aros/machine.h>
  32. #include <aros/libcall.h>
  33.  
  34. /*****************************************************************************
  35.  
  36.     NAME */
  37.     #include <clib/exec_protos.h>
  38.  
  39.     AROS_LH4I(APTR,RawDoFmt,
  40.  
  41. /*  SYNOPSIS */
  42.     AROS_LHA(STRPTR,    FormatString, A0),
  43.     AROS_LHA(APTR,      DataStream,   A1),
  44.     AROS_LHA(VOID_FUNC, PutChProc,    A2),
  45.     AROS_LHA(APTR,      PutChData,    A3),
  46.  
  47. /*  LOCATION */
  48.     struct ExecBase *, SysBase, 87, Exec)
  49.  
  50. /*  FUNCTION
  51.     printf-style formatting function with callback hook.
  52.  
  53.     INPUTS
  54.     FormatString - Pointer to the format string with any of the following
  55.                stream formatting options allowed:
  56.  
  57.                %[leftalign][minwidth.][maxwidth][size][type]
  58.  
  59.                leftalign - '-' means align left. Default: align right.
  60.                minwidth  - minimum width of field. Defaults to 0.
  61.                maxwidth  - maximum width of field (for strings only).
  62.                    Defaults to no limit.
  63.                size     - 'l' means longword. Defaults to word.
  64.                type     - 'b' BCPL string. A BPTR to a one byte
  65.                        byte count followed by the characters.
  66.                    'c' single character.
  67.                    'd' signed decimal number.
  68.                    's' C string. NUL terminated.
  69.                    'u' unsigned decimal number.
  70.                    'x' unsigned sedecimal number.
  71.  
  72.     DataStream   - Array of the data items.
  73.     PutChProc    - Callback function. Called for each character, including
  74.                the NUL terminator.
  75.     PutChData    - Data propagated to each call of the callback hook.
  76.  
  77.     RESULT
  78.     Pointer to the rest of the DataStream.
  79.  
  80.     NOTES
  81.     The field size defaults to words which may be different from the
  82.     default integer size of the compiler.
  83.  
  84.     EXAMPLE
  85.     build a sprintf style function
  86.  
  87.     static void callback(UBYTE chr, UBYTE **data)
  88.     {   *(*data)++=chr;   }
  89.  
  90.     void my_sprintf(UBYTE *buffer, UBYTE *format, ...)
  91.     {   RawDoFmt(format, &format+1, &callback, &buffer);   }
  92.  
  93.     BUGS
  94.     PutChData cannot be modified from the callback hook.
  95.  
  96.     SEE ALSO
  97.  
  98.     INTERNALS
  99.  
  100.     HISTORY
  101.     22-10-95    created by m. fleischer
  102.  
  103. ******************************************************************************/
  104. {
  105.     AROS_LIBFUNC_INIT
  106.     /* Cast for easier access */
  107.     ULONG stream=(IPTR)DataStream;
  108.  
  109.     /* As long as there is something to format left */
  110.     while(*FormatString)
  111.     {
  112.     /* Check for '%' sign */
  113.     if(*FormatString=='%')
  114.     {
  115.         /*
  116.         left     - left align flag
  117.         fill     - pad character
  118.         minus     - 1: number is negative
  119.         minwidth - minimum width
  120.         maxwidth - maximum width
  121.         larg     - long argument flag
  122.         width     - width of printable string
  123.         buf     - pointer to printable string
  124.         */
  125.         int left=0;
  126.         int fill=' ';
  127.         int minus=0;
  128.         ULONG minwidth=0;
  129.         ULONG maxwidth=~0;
  130.         int larg=0;
  131.         ULONG width=0;
  132.         UBYTE *buf;
  133.  
  134.         /*
  135.         Number of decimal places required to convert a unsigned long to
  136.         ascii. The formula is: ceil(number_of_bits*log10(2)).
  137.         Since I can't do this here I use .302 instead of log10(2) and
  138.         +1 instead of ceil() which most often leads to exactly the
  139.         same result (and never becomes smaller).
  140.  
  141.         Note that when the buffer is large enough for decimal it's
  142.         large enough for sedecimal as well.
  143.         */
  144. #define CBUFSIZE (sizeof(ULONG)*8*302/1000+1)
  145.         /* The buffer for converting long to ascii */
  146.         UBYTE cbuf[CBUFSIZE];
  147.         ULONG i;
  148.  
  149.         /* Skip over '%' character */
  150.         FormatString++;
  151.  
  152.         /* '-' modifier? (left align) */
  153.         if(*FormatString=='-')
  154.         left=*FormatString++;
  155.  
  156.         /* '0' modifer? (pad with zeros) */
  157.         if(*FormatString=='0')
  158.         fill=*FormatString++;
  159.  
  160.         /* Get minimal width */
  161.         if(*FormatString>='0'&&*FormatString<='9')
  162.         {
  163.         do
  164.             minwidth=minwidth*10+(*FormatString++-'0');
  165.         while(*FormatString>='0'&&*FormatString<='9');
  166.  
  167.         /* Dot following width modifier? */
  168.         if(*FormatString=='.')
  169.         {
  170.             FormatString++;
  171.             /* Get maximum width */
  172.             if(*FormatString>='0'&&*FormatString<='9')
  173.             {
  174.             maxwidth=0;
  175.             do
  176.                 maxwidth=maxwidth*10+(*FormatString++-'0');
  177.             while(*FormatString>='0'&&*FormatString<='9');
  178.             }
  179.         }
  180.         else
  181.         {
  182.             /* No. It was in fact a maxwidth modifier */
  183.             maxwidth=minwidth;
  184.             minwidth=0;
  185.         }
  186.         }
  187.  
  188.         /* 'l' modifier? (long argument) */
  189.         if(*FormatString=='l')
  190.         larg=*FormatString++;
  191.  
  192.         /* Switch over possible format characters. Sets minus, width and buf. */
  193.         switch(*FormatString)
  194.         {
  195.         /* BCPL string */
  196.         case 'b':
  197.             /* Get address, but align datastream first */
  198.             if(AROS_LONGALIGN>AROS_WORDALIGN)
  199.             stream=(stream+AROS_LONGALIGN-1)&~(AROS_LONGALIGN-1);
  200.             buf=(UBYTE *)BADDR(*(BPTR *)stream);
  201.             stream+=sizeof(BPTR);
  202.  
  203.             /* Set width */
  204.             width=*buf++;
  205.  
  206.             /* Strings may be modified with the maxwidth modifier */
  207.             if(width>maxwidth)
  208.             width=maxwidth;
  209.             break;
  210.  
  211.         /* signed decimal value */
  212.         case 'd':
  213.         /* unsigned decimal value */
  214.         case 'u':
  215.             {
  216.             ULONG n;
  217.  
  218.             /* Get value */
  219.             if(larg)
  220.             {
  221.                 /* Align datastream */
  222.                 if(AROS_LONGALIGN>AROS_WORDALIGN)
  223.                 stream=(stream+AROS_LONGALIGN-1)&~(AROS_LONGALIGN-1);
  224.                 /*
  225.                 For longs reading signed and unsigned
  226.                 doesn't make a difference.
  227.                 */
  228.                 n=*(ULONG *)stream;
  229.                 stream+=sizeof(ULONG);
  230.  
  231.                 /* But for words it may do. */
  232.             }else
  233.                 /*
  234.                 Sorry - it may not: Stupid exec always treats
  235.                 UWORD as WORD even when 'u' is used.
  236.                 */
  237. #ifdef FIX_EXEC_BUGS
  238.                 if(*FormatString=='d')
  239. #else
  240.                 if(1)
  241. #endif
  242.                 {
  243.                 n=*(WORD *)stream;
  244.                 stream+=sizeof(WORD);
  245.                 }else
  246.                 {
  247.                 n=*(UWORD *)stream;
  248.                 stream+=sizeof(UWORD);
  249.                 }
  250.  
  251.             /* Negative number? */
  252.             if(*FormatString=='d'&&(LONG)n<0)
  253.             {
  254.                 minus=1;
  255.                 n=-n;
  256.             }
  257.  
  258.             /* Convert to ASCII */
  259.             buf=&cbuf[CBUFSIZE];
  260.             do
  261.             {
  262.                 /*
  263.                 divide 'n' by 10 and get quotient 'n'
  264.                 and remainder 'r'
  265.                 */
  266.                 *--buf=(n%10)+'0';
  267.                 n/=10;
  268.                 width++;
  269.             }while(n);
  270.             }
  271.             break;
  272.  
  273.         /* unsigned sedecimal value */
  274.         case 'x':
  275.             {
  276.             ULONG n;
  277.  
  278.             /* Get value */
  279.             if(larg)
  280.             {
  281.                 /* Align datastream */
  282.                 if(AROS_LONGALIGN>AROS_WORDALIGN)
  283.                 stream=(stream+AROS_LONGALIGN-1)&~(AROS_LONGALIGN-1);
  284.                 n=*(ULONG *)stream;
  285.                 stream+=sizeof(ULONG);
  286.             }else
  287.             {
  288.                 n=*(UWORD *)stream;
  289.                 stream+=sizeof(UWORD);
  290.             }
  291.  
  292.             /* Convert to ASCII */
  293.             buf=&cbuf[CBUFSIZE];
  294.             do
  295.             {
  296.                 /*
  297.                 Uppercase characters for lowercase 'x'?
  298.                 Stupid exec original!
  299.                 */
  300.                 *--buf="0123456789ABCDEF"[n&15];
  301.                 n>>=4;
  302.                 width++;
  303.             }while(n);
  304.             }
  305.             break;
  306.  
  307.         /* C string */
  308.         case 's':
  309.             {
  310.             UBYTE *buffer;
  311.  
  312.             /* Get address, but align datastream first */
  313.             if(AROS_PTRALIGN>AROS_WORDALIGN)
  314.                 stream=(stream+AROS_PTRALIGN-1)&~(AROS_PTRALIGN-1);
  315.             buf=*(UBYTE **)stream;
  316.             stream+=sizeof(UBYTE *);
  317.  
  318.             /* width=strlen(buf) */
  319.             buffer=buf;
  320.             while(*buffer++)
  321.                 ;
  322.             width=~(buf-buffer);
  323.  
  324.             /* Strings may be modified with the maxwidth modifier */
  325.             if(width>maxwidth)
  326.                 width=maxwidth;
  327.             }
  328.             break;
  329.  
  330.         /* single character */
  331.         case 'c':
  332.             /* Some space for the result */
  333.             buf=cbuf;
  334.             width=1;
  335.  
  336.             /* Get value */
  337.             if(larg)
  338.             {
  339.             /* Align datastream */
  340.             if(AROS_LONGALIGN>AROS_WORDALIGN)
  341.                 stream=(stream+AROS_LONGALIGN-1)&~(AROS_LONGALIGN-1);
  342.             *buf=*(ULONG *)stream;
  343.             stream+=sizeof(ULONG);
  344.             }else
  345.             {
  346.             *buf=*(UWORD *)stream;
  347.             stream+=sizeof(UWORD);
  348.             }
  349.             break;
  350.  
  351.         /* '%' before '\0'? */
  352.         case '\0':
  353.             /*
  354.             This is nonsense - but do something useful:
  355.             Instead of reading over the '\0' reuse the '\0'.
  356.             */
  357.             FormatString--;
  358.             /* Get compiler happy */
  359.             buf=NULL;
  360.             break;
  361.  
  362.         /* Convert '%unknown' to 'unknown'. This includes '%%' to '%'. */
  363.         default:
  364.             buf=FormatString;
  365.             width=1;
  366.             break;
  367.         }
  368.         /* Skip the format character */
  369.         FormatString++;
  370.  
  371.         /*
  372.         Now everything I need is known:
  373.         buf     - contains the string to be printed
  374.         width     - the size of the string
  375.         minus     - is 1 if there is a '-' to print
  376.         fill     - is the pad character
  377.         left     - is 1 if the string should be left aligned
  378.         minwidth - is the minimal width of the field
  379.         (maxwidth is already part of width)
  380.  
  381.         So just print it.
  382.         */
  383.  
  384.         /*
  385.         Stupid exec always prints the '-' sign directly before
  386.         the decimals. Even if the pad character is a '0'.
  387.         */
  388. #ifdef FIX_EXEC_BUGS
  389.         /* Print '-' (if there is one and the pad character is no space) */
  390.         if(minus&&fill!=' ')
  391.         RDFCALL(PutChProc,'-',PutChData);
  392. #endif
  393.         /* Pad left if not left aligned */
  394.         if(!left)
  395.         for(i=width+minus;i<minwidth;i++)
  396.             RDFCALL(PutChProc,fill,PutChData);
  397.  
  398.         /* Print '-' (if there is one and the pad character is a space) */
  399. #ifdef FIX_EXEC_BUGS
  400.         if(minus&&fill==' ')
  401. #else
  402.         if(minus)
  403. #endif
  404.         RDFCALL(PutChProc,'-',PutChData);
  405.  
  406.         /* Print body upto width */
  407.         for(i=0;i<width;i++)
  408.         {
  409.         RDFCALL(PutChProc,*buf,PutChData);
  410.         buf++;
  411.         }
  412.  
  413.         /* Pad right if left aligned */
  414.         if(left)
  415.         for(i=width+minus;i<minwidth;i++)
  416.             /* Pad right with '0'? Sigh - if the user wants to! */
  417.             RDFCALL(PutChProc,fill,PutChData);
  418.     }else
  419.     {
  420.         /* No '%' sign? Put the formatstring out */
  421.         RDFCALL(PutChProc,*FormatString,PutChData);
  422.         FormatString++;
  423.     }
  424.     }
  425.     /* All done. Put the terminator out. */
  426.     RDFCALL(PutChProc,'\0',PutChData);
  427.  
  428.     /* Return the rest of the datastream. */
  429.     return (APTR)stream;
  430.     AROS_LIBFUNC_EXIT
  431. } /* RawDoFmt */
  432.  
  433.